home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / news / readers / nn-tk.001 / nn-tk~ / nn / account.c next >
C/C++ Source or Header  |  1995-07-25  |  13KB  |  599 lines

  1. /*
  2.  *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  3.  *
  4.  *    Accounting for news reading.
  5.  *
  6.  *    The nnacct program is called by nn it three cases:
  7.  *
  8.  *    - on startup (-q) to check for permission to run nn (at this time)
  9.  *    - when the :cost command is executed (-cUSAGE -r) to
  10.  *      produce a "cost sofar" report, and
  11.  *    - at exit (-uUSAGE -r) to add the USAGE to the user's account
  12.  *      and print a "cost" report.
  13.  *
  14.  *    It can also be invoked by nnusage to print a usage and cost
  15.  *    report for the current user (default), or by the super user
  16.  *    to produce a usage and cost report for all users.
  17.  *
  18.  *    Accumulated accounting information is saved in the file $DB/acct.
  19.  *
  20.  *    If ACCTLOG is defined, a sequential log is maintained in $DB/acctlog.
  21.  *
  22.  *    You can define a COST_PER_MINUTE and a COST_UNIT to make
  23.  *    the user get a cost calculation (maybe just for the fun of it -
  24.  *    you can't imagine how expensive it is to read news here :-).
  25.  *
  26.  *    The COST_PER_MINUTE should be the price per minute multiplied
  27.  *    by 100 (to allow prices like $0.03/minute).
  28.  *    The definitions below corresponds to 1 Kroner per minute.
  29.  *
  30.  *    If COST_PER_MINUTE is not defined (you can #undef it in config.h),
  31.  *    a "Time used" rather than a "Cost" report is produced.
  32.  */
  33.  
  34. #define ACCTLOG            /* */
  35. #define COST_PER_MINUTE    100    /* price(in UNITs)/min * 100 */
  36. #define COST_UNIT        "Kr."    /* Currency */
  37.  
  38. #include "config.h"
  39. #include "options.h"
  40. #include "proto.h"
  41.  
  42. import char *db_directory;
  43. extern int errno;
  44.  
  45. struct account {
  46.     off_t ac_offset;    /* offset in acct file */
  47.     int ac_found;    /* present in acct file */
  48.     
  49.     char ac_user[24];    /* user name */
  50.     
  51.     long ac_total;    /* total usage */
  52.     time_t ac_last;    /* last active */
  53.     int ac_policy;    /* assigned policy */
  54.     int ac_quota;    /* time quota */
  55. };
  56.  
  57. /* account.c */
  58.  
  59. static void put_entry __APROTO((FILE *acctf, struct account *ac));
  60. static int  get_entry __APROTO((FILE *acctf, char *user, struct account *ac));
  61. static void do_cost __APROTO((struct account *ac, int ses));
  62. static void do_report __APROTO((struct account *ac, int hdr));
  63. static void do_report_all __APROTO((FILE *acctf));
  64. static void do_zero __APROTO((void));
  65. static int  news_admin __APROTO((char *caller));
  66.  
  67. /*
  68.  *     local authorization policy checking
  69.  *
  70.  *    return/exit values of policy_check (nnacct -P0) are:
  71.  *
  72.  *    0: access granted
  73.  *    1: access granted, but cannot post
  74.  *    2: access denied (not authorized)
  75.  *    3: access denied (not allowed at this time of day)
  76.  *    4: access denied (quota exceeded)
  77.  */
  78.  
  79. #define DENY_ACCESS    0
  80. #define FREE_ACCOUNT    1
  81. #define ALL_HOURS    2
  82. #define OFF_HOURS    3
  83.  
  84. #define NO_POST        40    /* add if cannot post */
  85.  
  86. /*
  87.  *    DEFAULT POLICY AND QUOTA FOR NEW USERS
  88.  *
  89.  *    Notice that QUOTA is measued in hours.
  90.  *    Both ACCOUNTING and AUTHORIZATION must be defined for
  91.  *    the quota mechanism to work.
  92.  */
  93.  
  94. #define DEFAULT_POLICY     ALL_HOURS    /* all time w/accounting */
  95. #define DEFAULT_QUOTA     0        /* unlimited use */
  96.  
  97. #ifdef AUTHORIZE
  98.  
  99. #include <time.h>
  100.  
  101. static holiday(tm)
  102. struct tm *tm;
  103. {
  104.     /* Kim's birthday - 23 April */
  105.     if (tm->tm_mon == 3 && tm->tm_mday == 23) return 1;
  106.     /* another birthday - 25 December :-) */
  107.     if (tm->tm_mon == 11 && tm->tm_mday == 25) return 1;    
  108.     /* ... */
  109.     return 0;
  110. }
  111.  
  112. static policy_check(policy)
  113. int policy;
  114. {
  115.     struct tm *tm, *localtime();
  116.     time_t t;
  117.     int no_post = 0;
  118.  
  119.     if (policy >= NO_POST) {
  120.     policy -= NO_POST;
  121.     no_post = 1;
  122.     }
  123.     
  124.     switch (policy % 10) {
  125.      case DENY_ACCESS:
  126.     return 2;
  127.  
  128.      case ALL_HOURS:
  129.      case FREE_ACCOUNT:
  130.     break;
  131.  
  132.      case OFF_HOURS:    /* adapt this to your local requirements */
  133.     time(&t);
  134.     tm = localtime(&t);
  135.     if (tm->tm_wday == 0) break;    /* Sunday */
  136.     if (tm->tm_wday == 6) break;    /* Saturday */
  137.     if (tm->tm_hour < 9) break;    /* morning */
  138.     if (tm->tm_hour > 16) break;    /* evening */
  139.     if (holiday(tm)) break;        /* holidays */
  140.     /* etc. */
  141.     return 3;
  142.  
  143.      default:
  144.     return 2;
  145.     }
  146.  
  147.     return no_post;
  148. }
  149.  
  150. #endif
  151.  
  152. static int add_usage = -1;
  153. static int show_cost = -1;
  154. static int report = 0;
  155. static int report_all = 0;
  156. static int quiet = 0;
  157. static char *acct_file = NULL;
  158. static int ck_policy = -1;
  159. static int new_policy = -1;
  160. static int new_quota = -1;
  161. static int who_am_caller = I_AM_ACCT;
  162. static char *zero_accounts = NULL;
  163.  
  164. Option_Description(acct_options) {
  165.     'C', Int_Option(show_cost),
  166.     'U', Int_Option(add_usage),
  167.     'W', Int_Option(who_am_caller),
  168.     'P', Int_Option(ck_policy),
  169.     'a', Bool_Option(report_all),
  170.     'f', String_Option(acct_file),
  171.     'p', Int_Option(new_policy),
  172.     'q', Int_Option(new_quota),
  173.     'r', Bool_Option(report),
  174.     'Q', Bool_Option(quiet),
  175.     'Z', String_Option(zero_accounts),
  176.     '\0',
  177. };
  178.  
  179. /*
  180.  *    Accounting information:
  181.  *
  182.  *    xxxxxxx 00000000 00000000 00 00000\n
  183.  *
  184.  *    login    time used last     pol quota
  185.  *    name    (minutes) active icy (hours)
  186.  *
  187.  *    See the printf/scanf formats later on.
  188.  */
  189.  
  190.  
  191. #define INPUT_FMT    "%s %ld %lx %d %d\n"
  192. #define OUTPUT_FMT    "%s %08ld %08lx %02d %05d\n"
  193.  
  194. static get_entry(acctf, user, ac)
  195. FILE *acctf;
  196. char *user;
  197. struct account *ac;
  198. {
  199.     char line[100];
  200.  
  201.     if (acctf != NULL && user != NULL) 
  202.     rewind(acctf);
  203.     
  204.     ac->ac_found = 0;
  205.  
  206.     for (;;) {
  207.     ac->ac_policy = DEFAULT_POLICY;
  208.     ac->ac_last = 0;
  209.     ac->ac_total = 0;
  210.     ac->ac_quota = DEFAULT_QUOTA;
  211.     ac->ac_user[0] = NUL;
  212.     
  213.     if (acctf == NULL) break;
  214.     
  215.     ac->ac_offset = ftell(acctf);
  216.  
  217.     if (fgets(line, 100, acctf) == NULL) break;
  218.  
  219.     sscanf(line, INPUT_FMT, 
  220.            ac->ac_user, &ac->ac_total, &ac->ac_last, 
  221.            &ac->ac_policy, &ac->ac_quota);
  222.  
  223.     if (user == NULL) return 1;
  224.  
  225.     if (strcmp(user, ac->ac_user) == 0) {
  226.         ac->ac_found = 1;
  227.         return 1;
  228.     }
  229.     }
  230.  
  231.     if (user != NULL) strcpy(ac->ac_user, user);
  232.     return 0;
  233. }
  234.  
  235. static void
  236. put_entry(acctf, ac)
  237. FILE *acctf;
  238. struct account *ac;
  239. {
  240.     if (ac->ac_found)
  241.     fseek(acctf, ac->ac_offset, 0);
  242.     else
  243.     fseek(acctf, (off_t)0, 2);
  244.     
  245.     fprintf(acctf, OUTPUT_FMT, 
  246.         ac->ac_user, ac->ac_total, ac->ac_last,
  247.         ac->ac_policy, ac->ac_quota);
  248. }
  249.  
  250. static void
  251. do_cost(ac, ses)
  252. struct account *ac;
  253. int ses;
  254. {
  255.     long r;
  256.     
  257. #ifdef COST_PER_MINUTE
  258. #ifndef COST_UNIT
  259. #define COST_UNIT ""
  260. #endif
  261.     if (ses >= 0)
  262.     printf("Cost this session: %ld %s   Period total:",
  263.            ((long)ses * COST_PER_MINUTE) / 100, COST_UNIT);
  264.     printf("%6ld %s",
  265.        (ac->ac_total * COST_PER_MINUTE) / 100, COST_UNIT);
  266. #else
  267.     printf("Time used this session: %d.%02d   Period total: %ld.%02ld",
  268.        ses/60, ses%60, ac->ac_total/60, ac->ac_total%60);
  269. #endif
  270.     if (ses >= 0 && ac->ac_quota > 0) {
  271.     r = ac->ac_quota - ac->ac_total/60;
  272.     printf("  Quota: %ld hour%s", r, plural(r));
  273.     }
  274.     fl;
  275. }
  276.  
  277. static 
  278. void do_report(ac, hdr)
  279. struct account *ac;
  280. int hdr;
  281. {
  282.     if (hdr) {
  283.     printf("USER        USAGE  QUOTA  LAST_ACTIVE");
  284. #ifdef COST_PER_MINUTE
  285.     printf("   COST/PERIOD");
  286. #endif
  287. #ifdef AUTHORIZE
  288.     printf("   POLICY");
  289. #endif
  290.     putchar(NL);
  291.     }
  292.  
  293.     printf("%-8.8s  %4ld.%02ld  %5d  %-12.12s  ",
  294.        ac->ac_user,
  295.        ac->ac_total/60, ac->ac_total%60,
  296.        ac->ac_quota,
  297.        ac->ac_last ? date_time(ac->ac_last) : "");
  298. #ifdef COST_PER_MINUTE
  299.     do_cost(ac, -1);
  300. #endif
  301. #ifdef AUTHORIZE
  302.     printf("     %2d  ", ac->ac_policy);
  303. #endif
  304.     printf("\n");
  305. }
  306.  
  307. static void
  308. do_report_all(acctf)
  309. FILE *acctf;
  310. {
  311.     struct account ac;
  312.     int first = 1;
  313.     
  314.     while (get_entry(acctf, (char *)NULL, &ac)) {
  315.     do_report(&ac, first);
  316.     first = 0;
  317.     }
  318. }
  319.  
  320. static char *ZERO_STAMP = "(Zeroed)";
  321.  
  322. static void
  323. do_zero()
  324. {
  325.     FILE *old, *new;
  326.     char *acct, bak[FILENAME];
  327.     struct account ac;
  328.  
  329.     acct = relative(db_directory, "acct");
  330.     old = open_file(acct, OPEN_READ);
  331.     if (old == NULL) goto err;
  332.  
  333.     sprintf(bak, "%s.old", acct);
  334.     if (unlink(bak) < 0 && errno != ENOENT) goto err;
  335.     if (link(acct, bak) < 0) goto err;
  336.     if (unlink(acct) < 0) {
  337.     unlink(bak);
  338.     goto err;
  339.     }
  340.  
  341.     umask(0177);
  342.     new = open_file(acct, OPEN_CREATE);
  343.     if (new == NULL) goto err2;
  344.     ac.ac_found = 0;
  345.     strcpy(ac.ac_user, ZERO_STAMP);
  346.     ac.ac_total = ac.ac_policy = ac.ac_quota = 0;
  347.     time(&(ac.ac_last));
  348.     put_entry(new, &ac);
  349.     
  350.     while (get_entry(old, (char *)NULL, &ac)) {
  351.     if (strcmp(ac.ac_user, ZERO_STAMP) == 0) continue;
  352.     ac.ac_total = 0;
  353.     ac.ac_found = 0;
  354.     put_entry(new, &ac);
  355.     }
  356.     fclose(old);
  357.     if (fclose(new) == EOF) goto err2;
  358.     return;
  359.     
  360.  err2:
  361.     unlink(acct);
  362.     if (link(bak, acct) == 0) unlink(bak);
  363.  err:
  364.     fprintf(stderr, "ZERO of accounts failed -- check permissions etc.\n");
  365. }
  366.  
  367. static news_admin(caller)
  368. char *caller;
  369. {
  370.     FILE *adm;
  371.     char line[80];
  372.     int len, ok;
  373.  
  374.     adm = open_file(relative(lib_directory, "admins"), OPEN_READ);
  375.     if (adm == NULL) return 2;
  376.     len = strlen(caller);
  377.     ok = 0;
  378.  
  379.     while (fgets(line, 80, adm)) {
  380.     if (line[len] != NL) continue;
  381.     line[len] = NUL;
  382.     if (strcmp(caller, line) == 0) {
  383.         ok = 1;
  384.         break;
  385.     }
  386.     }
  387.     fclose(adm);
  388.     return ok;
  389. }
  390.  
  391. main(argc, argv)
  392. int argc;
  393. char *argv[];
  394. {
  395.     char *caller;
  396.     FILE *acctf;
  397.     char *fname = NULL;
  398.     int users, i;
  399.     struct account ac, *actab = NULL;
  400.     
  401.     who_am_i = I_AM_ACCT;
  402.  
  403.     init_global();
  404.  
  405.     users = parse_options(argc, argv, (char *)NULL, acct_options, "");
  406.  
  407.     if (zero_accounts && strcmp(zero_accounts, "ERO")) {
  408.     fprintf(stderr, "Must specify -ZERO to clear accounts\n");
  409.     exit(1);
  410.     }
  411.  
  412.     if (user_id != 0) {
  413.     caller = user_name();
  414.     if (news_admin(caller) == 1) goto caller_ok;
  415.  
  416.     if (report_all) {
  417.         fprintf(stderr, "Only root can request complete reports\n");
  418.         exit(9);
  419.     }
  420.     if (new_policy >= 0) {
  421.         fprintf(stderr, "Only root can change user authorization\n");
  422.         exit(9);
  423.     }
  424.     if (new_quota >= 0) {
  425.         fprintf(stderr, "Only root can change user quotas\n");
  426.         exit(9);
  427.     }
  428.     if (zero_accounts) {
  429.         fprintf(stderr, "Only root can zero user accounts\n");
  430.         exit(9);
  431.     }
  432.     if (users > 0) {
  433.         fprintf(stderr, "Only root can request reports for other users\n");
  434.         exit(9);
  435.     }
  436.     } else
  437.     caller = "root";
  438.  
  439.  caller_ok:
  440.     if ((new_policy >= 0 || new_quota >= 0) && users == 0) {
  441.     fprintf(stderr, "usage: %s -pPOLICY -qQUOTA user...\n", argv[0]);
  442.     exit(1);
  443.     }
  444.     
  445.     if (add_usage == 0 && report) {
  446.     show_cost = 0;
  447.     add_usage = -1;
  448.     }
  449.  
  450.     if (zero_accounts || add_usage > 0 || new_policy >= 0 || new_quota >= 0) {
  451.     if (acct_file) {
  452.         fprintf(stderr, "Can only update current acct file\n", acct_file);
  453.         exit(2);
  454.     }
  455.  
  456.     proto_lock(I_AM_ACCT, PL_SET_QUICK);
  457.     }
  458.  
  459.     if (zero_accounts) {
  460.     do_zero();
  461.     proto_lock(I_AM_ACCT, PL_CLEAR);
  462.     exit(0);
  463.     }
  464.  
  465.     if (acct_file) {
  466.     if ((acctf = open_file(acct_file, OPEN_READ)) == NULL)
  467.         acctf = open_file(relative(db_directory, acct_file), OPEN_READ);
  468.     if (acctf == NULL) {
  469.         fprintf(stderr, "Accounting file %s not found\n", acct_file);
  470.         if (add_usage > 0 || new_policy >= 0 || new_quota >= 0)
  471.         proto_lock(I_AM_ACCT, PL_CLEAR);
  472.         exit(1);
  473.     }
  474.     } else {
  475.     fname = relative(db_directory, "acct");
  476.         acctf = open_file(fname, OPEN_READ);
  477.     }
  478.  
  479.     if (report_all) {
  480.     do_report_all(acctf);
  481.     fclose(acctf);
  482.     exit(0);
  483.     }
  484.     
  485.     if (ck_policy >= 0) {
  486. #ifdef AUTHORIZE
  487.     get_entry(acctf, caller, &ac);
  488. #ifdef ACCOUNTING
  489.     if (ac.ac_quota > 0 && ac.ac_quota < ac.ac_total/60) exit(4);
  490. #endif
  491.     exit(policy_check(ac.ac_policy));
  492. #else
  493.     exit(0);
  494. #endif
  495.     }
  496.     
  497.     if (show_cost >= 0) {
  498.     get_entry(acctf, caller, &ac);
  499.     if (ac.ac_policy == FREE_ACCOUNT) exit(0);
  500.     ac.ac_total += show_cost;
  501.     do_cost(&ac, show_cost);
  502.     exit(0);
  503.     }
  504.     
  505.     if (add_usage > 0) {
  506.     get_entry(acctf, caller, &ac);
  507.     if (ac.ac_policy == FREE_ACCOUNT) goto unlock;
  508.     ac.ac_total += add_usage;
  509.     time(&ac.ac_last);
  510.     } else
  511.     if (users > 0) {
  512.     actab = newobj(struct account, users + 1);
  513.     for (i = 1; i <= users; i++) {
  514.         get_entry(acctf, argv[i], &actab[i]);
  515.         if (new_policy >= 0 || new_quota >= 0) {
  516.         if (new_policy >= 0)
  517.             actab[i].ac_policy = new_policy;
  518.         if (new_quota >= 0)
  519.             actab[i].ac_quota = new_quota;
  520.         } else
  521.         do_report(&actab[i], i == 1);
  522.     }
  523.     } else
  524.     if (report) {
  525.     if (get_entry(acctf, caller, &ac))
  526.         do_report(&ac, 1);
  527.     exit(0);
  528.     }
  529.     
  530.     if (acctf) fclose(acctf);
  531.  
  532.     if (add_usage <= 0 && new_policy < 0 && new_quota < 0) exit(0);
  533.     
  534.     umask(0177);
  535.     acctf = open_file(fname, OPEN_UPDATE | MUST_EXIST);
  536.     
  537.     if (new_policy >= 0 || new_quota >= 0) {
  538.     for (i = 1; i <= users; i++)
  539.         put_entry(acctf, &actab[i]);
  540.     fclose(acctf);
  541.     goto unlock;
  542.     }
  543.  
  544.     if (add_usage > 0) {
  545.     put_entry(acctf, &ac);
  546.     if (report) {
  547.         do_cost(&ac, add_usage);
  548.     }
  549.     fclose(acctf);
  550. #ifdef ACCTLOG
  551.     fname = relative(db_directory, "acctlog");
  552.         acctf = open_file(fname, OPEN_APPEND | MUST_EXIST);
  553.     fprintf(acctf, "%s\t%s\t%ld\n",
  554.         caller, date_time(ac.ac_last), (long)add_usage);
  555.     fclose(acctf);
  556. #endif
  557.     goto unlock;
  558.     }
  559.  
  560.  unlock:
  561.     proto_lock(I_AM_ACCT, PL_CLEAR);
  562.     exit(0);
  563.     /*NOTREACHED*/
  564. }
  565.  
  566. void
  567. nn_exit(n)
  568. int n;
  569. {
  570.     exit(n);
  571. }
  572.  
  573. /*VARARGS*/
  574. void nn_exitmsg(va_alist)
  575. va_dcl
  576. {
  577.     char *fmt;
  578.     int n;
  579.     use_vararg;
  580.  
  581.     start_vararg;
  582.     n = va_arg1(int);
  583.     fmt = va_arg2(char *);
  584.     vprintf(fmt, va_args3toN);
  585.     putchar(NL);
  586.     end_vararg;
  587.  
  588.     nn_exit(n);
  589.     /*NOTREACHED*/
  590. }
  591.  
  592. #ifdef HAVE_JOBCONTROL
  593. int
  594. suspend_nn()
  595. {
  596.   return 0;
  597. }
  598. #endif
  599.